home *** CD-ROM | disk | FTP | other *** search
/ L' Effet Pommier 3 / L'Effet Pommier - Volume 03.iso / Programmation / Alpha ƒ / Tcl / SystemCode / bibtex.tcl < prev    next >
Text File  |  1995-07-14  |  44KB  |  1,385 lines

  1. ###########################################################################
  2. # bibtex.tcl
  3. # This file contains a package of Tcl routines that add support for using
  4. # and maintaining BibTeX citation databases to Alpha. 
  5. #
  6. # See the accompanying file, "BibTeX Help", for a complete description.
  7. # (Unfortunately, it's a bit out of date right now - stay tuned (WTP 6/95))
  8. ###########################################################################
  9. # Notes: 
  10. # By default, only the required fields are included when a new bib entry 
  11. # is created.  You can select any other set of fields to be used by adding 
  12. # an appropriate entry to the 'myFld' array, following the example for the 
  13. # Article entry, further below.  You shouldn't change the 'rqdFld' or 
  14. # 'optFld' arrays, since these will (some day) be used for syntax checking.
  15. ###########################################################################
  16. # written by Tom Pollard (pollard@cucbs.chem.columbia.edu)
  17. #
  18. # Version History
  19. #
  20. # 2.7  (7/95)   'stdAbbrevs' modeVar added for setting predefined abbrevs
  21. #               month names included as predefined abbrevs
  22. #               'alignEquals' formatting flag added.
  23. # 2.62 (7/95)   field delimiters suppressed if field data is an abbreviation
  24. #               unindexed .bib files are indexed automatically upon opening
  25. # 2.61 (7/95)   fixed "SearchFields" bug.
  26. # 2.6  (6/95)   'zapEmptyFields' flag forces optional fields to be removed 
  27. #                   when reformatting an entry.
  28. #               'markStrings' flag controls whether @string entries are included in 
  29. #                   the marks menu.
  30. #               'descendingYears' flag controls whether sorts are in ascending or 
  31. #                   descending chronological order.
  32. #               Sorts all use the year as either primary or secondary sort key now.
  33. #               'copyCiteKey' command copies the citekey of the current entry to the
  34. #                   clipboard.
  35. #               Cmd-double-clicking implemented to resolve abbreviations and crossrefs.
  36. #               Fixed bug in faster getFields proc (comma-after-last-field problems)  
  37. #               Fixed minor bugs in author sorting.  
  38. # 2.5  (6/95)   Fixed bug in formatEntry, whereby '#' concatenations were lost 
  39. #               formatEntry completely ignores @string entries now
  40. #               Entry-parsing code (getFields, getFldVal) cleaned up,
  41. #                  should also be a little bit faster now.
  42. #               formatAllEntries now starts working from the current entry
  43. # 2.41 (6/95)   Updates for compatibility with revised LaTeX mode
  44. #               Automatic conversion of international characters dropped 
  45. #                   (irreconcilable problems with non-US keyboards).
  46. # 2.4  (5/95)   Fixed bugs in parsing of EndNote-created bib files
  47. # 2.3  (4/95)   International characters converted to TeX codes (optionally).
  48. #               'findEntries' bug fixed (no longer returns multiple hits) 
  49. # 2.2 (12/94)   'formatEntries' won't quote fields that contain "#".
  50. #               'segregateStrings' flag forces string defs to sort to the top.
  51. # 2.11(12/94)   Bug fixes in 'formatAllEntries'.
  52. # 2.1 (12/94)   'countEntries' command added.
  53. #               'formatAllEntries' command added; it's a bit clunky, but more robust
  54. #                   than any quicker alternative I considered.
  55. #               Cross-referenced entries now sort to the bottom in all sorts.
  56. #                'crossref' field now included.
  57. # 2.0  (9/94)   'formatEntry' and 'newEntry' line up fields better.
  58. #                'nextEntry' and 'prevEntry' skip @string defs
  59. #                'formatEntry' automtically goes to next entry afterwards.
  60. #                'sortByCitekey' ignores case of cite keys.
  61. #               'fillColumn' included as default modeVar.
  62. #                'getEntry' alerts user to badly delimited entries.
  63. # 1.9 (9/94)    'getFields' should now correctly parse any legal entry.
  64. #                'language' field now included.
  65. #                Default values for new fields (eg 'language') may be defined
  66. #                'preferBraces' replaced by 'fieldBraces' and 'entryBraces'.
  67. #                line-wrapping is done on reformatted entries.
  68. #                '@string' entries preserved in sorts.
  69. #                text before first entry and after last entry are preserved
  70. #                    by sorts.
  71. # 1.8 (8/94)    "getEntry" now recognizes parens as entry delimiters
  72. # 1.7 (8/94)    Bug fixes and accomodations to latex.tcl v2.2
  73. #               Template insertion streamlined
  74. #                Choose multiple fields at a time from list dialog
  75. # 1.6 (8/94)    "preferBraces" allows braces or quotes to be default for
  76. #                   new or reformatted entries,
  77. #               Menu built using $entryNames and $fieldNames,
  78. #               'sortByAuthors' can now sort using last author first,
  79. #                   and is a bit faster,
  80. #               'formatEntry' rewrites entries in canonical format,
  81. #               More customization of canonical format allowed ('indentString')
  82. #               Bib mode definition adapted to Alpha 5.90.
  83. # 1.5 (7/94)    "sortByAuthors" is now robust (I think),
  84. #               Mode of new windows now set correctly.
  85. # 1.4 (7/94)    Added sorting by authors, but still only semi-functional,
  86. #               Added regexp searching by field,
  87. #               "getEntry" bugs fixed.
  88. # 1.2 (7/94)    Bib mode definition adapted to Alpha 5.85,
  89. #               Added bib-file marking (bibMarkFile),
  90. #               Entry and field creation now controlled by data arrays.
  91. # 1.1 (6/94)    Custom BibTeX icon, 
  92. #               Added simple search capability (matchingEntries).
  93. # 1.0 (9/93)    First stable version.
  94. #
  95. ###########################################################################
  96. # This package was inspired by the LaTeX package (latex.tcl), written by
  97. #    Richard T. Austin  <austin@eecs.umich.edu>  , and (currently),
  98. #    Tom Scavo          <trscavo@syr.edu>
  99. #
  100. ###########################################################################
  101. ############################################################################
  102. # Cause latex.tcl to be loaded by calling a dummy procedure defined in that
  103. # file.  This is necessary to get the TeX menu, and to load the 8-bit ASCII
  104. # to TeX conversion routines.
  105. #
  106. dummyTeX
  107.  
  108. ###########################################################################
  109. # BibTeX Key Bindings.
  110. ###########################################################################
  111. # abbreviations:  <o> = option, <z> = control, <s> = shift, <c> = command
  112. #
  113. bind 'b' <sz>    selectEntry "Bib"
  114. bind 'n' <sz>    nextEntry "Bib"
  115. bind 'p' <sz>    prevEntry "Bib"
  116.  
  117. bind 'f' <sz>    searchFields "Bib"
  118. bind 'm' <sz>    searchEntries "Bib"
  119. bind 'l' <sz>    formatEntry "Bib"
  120.  
  121. # tab stops:
  122. bind    '\t'    nextTabStop    "Bib"
  123. bind    '\t'    <s>     prevTabStop    "Bib"
  124. bind  '\t'  <z>  {nthTabStop 0}  "Bib"
  125. bind  '\t'  <c>  deleteTabStops  "Bib"
  126.  
  127. ###########################################################################
  128. # Data Definitions
  129. ###########################################################################
  130. ###########################################################################
  131. # Define the data arrays that contain the names of the required,
  132. # optional, and preferred fields for each entry type.
  133. #
  134. # The index names of the rqdFld() array _define_ the valid entry types
  135. # recognized by the program.
  136. #
  137. set rqdFld(article) {author title journal year} 
  138. set optFld(article) {volume number pages month note}
  139. set myFld(article) {author title journal volume pages year note} 
  140.  
  141. set rqdFld(book) {author title publisher year} 
  142. set optFld(book) {editor volume number series address edition month note}
  143.  
  144. set rqdFld(booklet) {title} 
  145. set optFld(booklet) {author howpublished address month year note}
  146.  
  147. set rqdFld(conference) {author title booktitle year} 
  148. set optFld(conference) {editor volume number series pages organization publisher address month note}
  149.  
  150. set rqdFld(inBook) {author title chapter publisher year} 
  151. set optFld(inBook) {editor pages volume number series address edition month type note}
  152.  
  153. set rqdFld(inCollection) {author title booktitle publisher year} 
  154. set optFld(inCollection) {editor volume number series type chapter pages address edition month note}
  155.  
  156. set rqdFld(inProceedings) {author title booktitle year} 
  157. set optFld(inProceedings) {editor volume number series pages organization publisher address month note}
  158.  
  159. set rqdFld(manual) {title} 
  160. set optFld(manual) {author organization address edition year month note}
  161.  
  162. set rqdFld(mastersThesis) {author title school year} 
  163. set optFld(mastersThesis) {address month note type}
  164.  
  165. set rqdFld(misc) {} 
  166. set optFld(misc) {author title howpublished year month note}
  167.  
  168. set rqdFld(phdThesis) {author title school year} 
  169. set optFld(phdThesis) {address month type note}
  170.  
  171. set rqdFld(proceedings) {title year} 
  172. set optFld(proceedings) {editor volume number series publisher organization address month note}
  173.  
  174. set rqdFld(techReport) {author title institution year} 
  175. set optFld(techReport) {type number address month note}
  176.  
  177. set rqdFld(unpublished) {author title note} 
  178. set optFld(unpublished) {year month}
  179.  
  180. set entryNames [lsort [array names rqdFld]]
  181. set customEntries [lsort [array names myFld]]
  182.  
  183. ###########################################################################
  184. # Define an array of flags indicating whether the data a given field
  185. # type should be quoted.  The actual characters used to quote the field are
  186. # given by $bibOpenQuote and $bibCloseQuote, which are set by the routine
  187. # 'bibFieldDelims' according to the flag $fieldBraces.
  188. #
  189. # Note that the index names of the useBrace() array _define_ the valid 
  190. # field types recognized by the program.
  191. #
  192. set useBrace(address)    1
  193. set useBrace(annote)    1
  194. set useBrace(author)     1
  195. set useBrace(booktitle)    1
  196. set useBrace(chapter)    0
  197. set useBrace(crossref)    1
  198. set useBrace(edition)    1
  199. set useBrace(editor)    1
  200. set useBrace(howpublished)    1
  201. set useBrace(institution)    1
  202. set useBrace(journal)    1
  203. set useBrace(key)    1
  204. set useBrace(language)    1
  205. set useBrace(month)    1
  206. set useBrace(note)    1
  207. set useBrace(number)    0
  208. set useBrace(organization)    1
  209. set useBrace(pages)    0
  210. set useBrace(publisher)    1
  211. set useBrace(school)    1
  212. set useBrace(series)    1
  213. set useBrace(title)    1
  214. set useBrace(type)    1
  215. set useBrace(volume)    0
  216. set useBrace(year)    0
  217.  
  218. set fieldNames [lsort [array names useBrace]]
  219. ###########################################################################
  220. # Default values for newly created fields
  221. #
  222. set defFldVal(language) "german"
  223.  
  224. set fieldDefs [lsort [array names defFldVal]]
  225.  
  226. ###########################################################################
  227. # Search patterns for entries and cite-keys
  228. #
  229. #     set bibTopPat {^[     ]*@[a-zA-Z]+[\{\(]([-A-Za-z0-9_:/\.]+)}
  230. # match entry type
  231. set bibTopPat {^[     ]*@([a-zA-Z]+)[\{\(]}
  232. # match cite-key
  233. set bibTopPat1 {^[     ]*@[a-zA-Z]+[\{\(][     ]*([^=,     ]+)}    
  234. # match type and cite-key
  235. set bibTopPat2 {^[     ]*@([a-zA-Z]+)[\{\(][     ]*([^=,     ]+)}    
  236. # match first field (no cite-key)
  237. set bibTopPat3 {^[     ]*@([a-zA-Z]+)[\{\(]([     ]*[a-zA-Z]+[     ]*=[     ]*)}    
  238.                                                                 
  239.  
  240. ###########################################################################
  241. # BibTeX-mode mode definition
  242. ###########################################################################
  243. newModeVar Bib suffixString    { \\\\} 0
  244. newModeVar Bib prefixString    {% } 0
  245. newModeVar Bib fillColumn    {65} 0
  246. newModeVar Bib wordWrap        {0} 1
  247. newModeVar Bib autoMark        {1} 1
  248.  
  249. newModeVar Bib wordBreak         {[a-zA-Z0-9]+} 0
  250. newModeVar Bib wordBreakPreface     {[^a-zA-Z0-9]} 0
  251. newModeVar Bib funcExpr           $bibTopPat 0
  252.  
  253. newModeVar Bib overwriteBuffer {1} 1
  254. newModeVar Bib fieldBraces {1} 1
  255. newModeVar Bib entryBraces {1} 1
  256. newModeVar Bib segregateStrings {1} 1
  257. newModeVar Bib markStrings {0} 1
  258. newModeVar Bib alignEquals {0} 1
  259. ###
  260. # newModeVar Bib emacsBibMode {0} 1
  261. # newModeVar Bib addCiteKeys {0} 1
  262. # newModeVar Bib checkSyntax {0} 1
  263. newModeVar Bib zapEmptyFields {0} 1
  264. newModeVar Bib descendingYears {1} 1
  265. ###
  266. newModeVar Bib indentString {   } 0
  267. newModeVar Bib stdAbbrevs {jan feb mar apr may jun jul aug sep oct nov dec} 0
  268. # newModeVar Bib convert8bitAscii2TeX {0} 1
  269.  
  270. set bibtexKeyWords {address annote author booktitle 
  271.     chapter city crossref edition editor howpublished institution 
  272.     journal key language month note number organization 
  273.     publisher pages school series title type 
  274.     volume year}
  275. regModeKeywords -e {%} -m {@} -c red -k blue Bib $bibtexKeyWords
  276. unset bibtexKeyWords
  277.  
  278. # # Use a shadow proc to keep settings for 8-bit character conversion 
  279. # # consistent between TeX and Bib modes. 
  280. # #
  281. # trace variable BibmodeVars(convert8bitAscii2TeX) w shadowBib8bitConvert
  282. # proc shadowBib8bitConvert {name1 name2 op} {
  283. #     global BibmodeVars TeXmodeVars
  284. #     # Use TeX-mode routines to actually do the key bindings. 
  285. #     #
  286. #     if {$BibmodeVars(convert8bitAscii2TeX)} then {
  287. #         toggle8bitAscii "ascii" "Bib"
  288. #     } else {
  289. #         toggle8bitAscii "unascii" "Bib"
  290. #     }
  291. #     
  292. #     # Only set TeX flag if necessary, to avoid unnecessary rebinding of keys
  293. #     # (It takes enough time to be annoying)
  294. #     #
  295. #     if {$BibmodeVars(convert8bitAscii2TeX) != $TeXmodeVars(convert8bitAscii2TeX)} then {
  296. #         set TeXmodeVars(convert8bitAscii2TeX) $BibmodeVars(convert8bitAscii2TeX)
  297. #     }
  298. # }
  299. # set BibmodeVars(convert8bitAscii2TeX) $TeXmodeVars(convert8bitAscii2TeX)
  300.  
  301. ###########################################################################
  302. # BibTeX Menu Definition.
  303. ###########################################################################
  304. proc bibtexMenu {} {}
  305.  
  306. set bibtexMenu "Ñ136"
  307.  
  308. proc bibtex {} {
  309.     global bibtexPath
  310.     set name [checkRunning BibTeX BIBt bibtexPath]
  311.     if {![string length $name]} return
  312.     switchTo $name
  313. }
  314.  
  315. proc makeindex {} {
  316.     global makeindexPath
  317.     set name [checkRunning MakeIndex Midx makeindexPath]
  318.     if {![string length $name]} return
  319.     switchTo $name
  320. }
  321.  
  322. menu -n $bibtexMenu {
  323.     "bibtex"
  324.     "(-)"  
  325.     {menu -n Entries -p makeEntry {}
  326.     }
  327.     {menu -n Fields -p makeField {}
  328.     }
  329.     "(-)"
  330.     "selectEntry/B<U<B"
  331.     "nextEntry/N<U<B"
  332.     "prevEntry/P<U<B"
  333.     "formatEntry/L<U<B"
  334.     "copyCiteKey/C<U<B"
  335.     "(-)"
  336.     "searchEntries/M<U<B"
  337.     "searchFields/F<U<B"
  338.     {menu -n sortBy... -p bibSortProc {
  339.         "citeKey"
  340.         "firstAuthor,Year"
  341.         "lastAuthor,Year"
  342.         "year,FirstAuthor"
  343.         "year,LastAuthor"
  344.         }
  345.     }
  346.     {menu -n sortMarks... -p markSortProc {
  347.         "alphabetically"
  348.         "byPosition"
  349.         }
  350.     }
  351.     "(-)"
  352.     "countEntries"
  353.     "formatAllEntries"
  354.     
  355. menu -n Entries -p makeEntry [concat $entryNames {
  356.         "(-)"
  357.         "customEntry"
  358.         } ]
  359.  
  360. menu -n Fields -p makeField [concat $fieldNames {
  361.         "(-)"
  362.         "customField"
  363.         "multipleFields"
  364.         } ]
  365.         
  366. ###########################################################################
  367. # Menu command procs
  368. ###########################################################################
  369.         
  370. proc makeField {menu item} {
  371.     global fieldNames
  372.     bibFormatSetup
  373.     
  374.     if {$item == "multipleFields"} then {
  375.         set flds [listpick -l -L {author year} -p "Pick desired fields:" $fieldNames]
  376.         if {[llength flds]} {
  377.             set lines {}
  378.             foreach fld $flds {
  379.                 append lines [newField $fld]
  380.             }
  381.         } else {
  382.             return
  383.         }
  384.     } else {
  385.         set lines [newField $item]
  386.     }
  387.     
  388.     set pos0 [nextLineStart [getPos]]
  389.     goto $pos0
  390.     insertText $lines
  391.     goto $pos0
  392.     nextTabStop
  393. }
  394.  
  395. proc makeEntry {menu item} {
  396.     bibFormatSetup
  397.     newEntry $item
  398. }
  399.  
  400. ###########################################################################
  401. #  Return the bounds of the bibliographic entry surrounding the current 
  402. #  position.
  403. #
  404. proc getEntry {pos} {
  405.     
  406.      set pos1 [search -f 0 -r 1 -n -s {[     ]*@[a-zA-Z]*[\{\(]} $pos ]
  407.     if {$pos1 == ""} then {
  408.         set begPos [nextLineStart $pos]
  409.         set endPos $begPos
  410.     } else {
  411.         set begPos [lineStart [lindex $pos1 0]]
  412.         set pos0 [lindex $pos1 1]
  413.         set openBrace [getText [expr $pos0-1] $pos0 ]
  414.         if {[catch {matchIt $openBrace $pos0]} pos1]} {
  415.             alertnote "There seems to be a badly delimited field in here.  Are entry and field delimiters set correctly?"
  416.             goto $begPos
  417.             error "Can't find close brace"
  418.         } else {
  419.             set endPos [nextLineStart $pos1]
  420.         }
  421.     }
  422.     return [list $begPos $endPos]
  423. }
  424.  
  425. ###########################################################################
  426. #  Advance to the next bibliographic entry.
  427. #
  428. proc nextEntry {} {
  429.     global bibTopPat bibTopPat1 bibTopPat2
  430. #     set topPat {[     ]*@([a-zA-Z]+)[\{\(]}
  431.     
  432.     set pos0 [lindex [getEntry [getPos]] 1]
  433.     set nextPos [nextLineStart $pos0]
  434.     
  435.     while {![catch {search -f 1 -r 1 -s $bibTopPat $pos0} pos]} {
  436.         regexp $bibTopPat [eval getText $pos] mtch type
  437.         if {$type != "string"} {
  438.             set nextPos [lindex $pos 0]
  439.             break
  440.         } else {
  441.             set pos0 [nextLineStart [lindex $pos 1]]
  442.         }
  443.     }
  444.     goto $nextPos
  445. }
  446.  
  447. ###########################################################################
  448. #  Go back to the previous bibliographic entry.
  449. #
  450. proc prevEntry {} {
  451.     global bibTopPat bibTopPat1 bibTopPat2
  452. #     set topPat {[     ]*@([a-zA-Z]+)[\{\(]}
  453.     
  454.     set pos0 [lindex [getEntry [getPos]] 0]
  455.     if {$pos0 > 0} {
  456.         set nextPos $pos0
  457.         incr pos0 -1
  458.         while {![catch {search -f 0 -r 1 -s $bibTopPat $pos0} pos]} {
  459.             regexp $bibTopPat [eval getText $pos] mtch type
  460.             if {$type != "string"} {
  461.                 set nextPos [lindex $pos 0]
  462.                 break
  463.             } else {
  464.                 set pos0 [lineStart [lindex $pos 0]]
  465.                 if {$pos0 == 0} {break}
  466.                 incr pos0 -1
  467.             }
  468.         }
  469.         goto $nextPos
  470.     }
  471. }
  472.  
  473. ###########################################################################
  474. #  Select (highlight) the current bibliographic entry.
  475. #
  476. proc selectEntry {} {
  477.     set pos [getEntry [getPos]]
  478.     select [lindex $pos 0] [lindex $pos 1]
  479. }
  480.  
  481. ###########################################################################
  482. #  Put the cite-key of the current entry on the clipboard.
  483. #
  484. proc copyCiteKey {} {
  485.     global bibTopPat2
  486.     set limits [getEntry [getPos]]
  487.     set top [lindex $limits 0]
  488.     set bottom [lindex $limits 1]
  489.     if {[regexp -indices $bibTopPat2 [getText $top $bottom] allofit type citekey]} {
  490.         select [expr $top+[lindex $citekey 0]] [expr $top+[lindex $citekey 1]+1]
  491.         copy
  492.         message "Copied \"[getSelect]\""
  493.     } 
  494. }
  495.  
  496. ###########################################################################
  497. #  Create a new bibliographic entry with its required fields.
  498. #
  499. proc newEntry {entryName} {    
  500.     global  entryNames customEntries fieldNames rqdFld optFld myFld defFldVal
  501.     global bibOpenEntry bibCloseEntry BibmodeVars
  502.     goto [lindex [getEntry [getPos]] 1]
  503.     if {$entryName == "customEntry"} {
  504.         set lines "@Ñ$bibOpenEntryÑ,\r"
  505.         set theFields [listpick -l -L {author} -p "Pick desired fields:" $fieldNames]
  506.     } else {
  507.         set lines "@${entryName}$bibOpenEntryÑ,\r"
  508.         if {[lsearch -exact $customEntries $entryName] >= 0 && [llength $myFld($entryName)]} {
  509.             set theFields $myFld($entryName)
  510.         } elseif {[lsearch -exact $entryNames $entryName] >= 0} {
  511.             set theFields $rqdFld($entryName)
  512.         } else {
  513.             set theFields {}
  514.         }
  515.     }
  516.     set nmlen 0
  517.     foreach field $theFields {
  518.         set len [string length $field]
  519.         if {$len > $nmlen} {set nmlen $len}        
  520.     }
  521.     set theTop [lineStart [getPos]]
  522.     foreach field $theFields {
  523.         catch {append lines [newField $field $nmlen]}
  524.     }
  525.     append lines "$bibCloseEntry\r"
  526.     insertText $lines
  527.     goto $theTop
  528.     nextTabStop
  529. }
  530.  
  531. ###########################################################################
  532. #  Create a new field within the current bibliographic entry
  533. #
  534. proc newField {fieldName {nmlen 0}} {    
  535.     global fieldNames useBrace bibOpenQuote bibCloseQuote bibIndent
  536.     global fieldDefs defFldVal
  537.     set spc "                   "
  538.     if {[lsearch -exact $fieldNames $fieldName] >= 0} {
  539.         set needBraces $useBrace($fieldName)
  540.     } else {
  541.         set needBraces 1
  542.     }
  543.     
  544.     if {[lsearch -exact $fieldDefs $fieldName] >= 0} {
  545.         set val $defFldVal($fieldName)
  546.     } else {
  547.         set val "Ñ"
  548.     }
  549.     
  550.     if {$nmlen} {
  551.         set pad [string range $spc 1 [expr $nmlen - [string length $fieldName]]]
  552.     } else {
  553.         set pad ""
  554.     }            
  555.     if {$needBraces || $fieldName == "customField"} {
  556.         set result "$bibIndent$fieldName =$pad ${bibOpenQuote}${val}${bibCloseQuote},\r"
  557.     } else {
  558.         set result "$bibIndent$fieldName =$pad $val,\r"
  559.     }    
  560.     return $result
  561. }
  562.  
  563. proc bibFormatSetup {} {
  564.     global bibOpenQuote bibCloseQuote bibIndent BibmodeVars
  565.     global bibOpenEntry bibCloseEntry bibAbbrevs
  566.     bibFieldDelims
  567.     bibEntryDelims
  568.     set bibIndent $BibmodeVars(indentString)
  569.     regsub {\\t} $bibIndent {    } bibIndent
  570.     set bibAbbrevs [listStrings]
  571.     foreach abbrev $BibmodeVars(stdAbbrevs) {
  572.         lappend bibAbbrevs [string tolower $abbrev]
  573.     }
  574. }
  575.  
  576. ###########################################################################
  577. #  Find all entries that match a given regular expression and copy them to 
  578. #  a new buffer.
  579. #
  580. proc searchEntries {} {
  581.     if [catch {prompt "Regular expression:" ""} reg] return
  582.     if {![string length $reg]} return
  583.     set reg ^.*$reg.*$
  584.     
  585.     set matches [findEntries $reg]
  586.     if {[llength $matches] >0} {
  587.         writeEntries $matches 0
  588.     } else {
  589.         message "No matching entries were found"
  590.     }
  591. }
  592.  
  593. ###########################################################################
  594. #  Find all entries in which the indicated field matches a given regular 
  595. #  expression and copy them to a new buffer.  
  596. #
  597. proc searchFields {} {
  598.     global fieldNames
  599.     if {[catch {eval prompt {{Field name:}} "author" {Fields} $fieldNames} fld]} return
  600.     if {![string length $fld]} return
  601.  
  602.     if {[catch {prompt "Regular expression:" ""} reg]} return
  603.     if {![string length $reg]} return
  604.  
  605.     set matches [findEntries $reg]
  606.     if {[llength $matches] == 0} {
  607.         return "No matching entries were found"
  608.     }
  609.     
  610.     set vals {}
  611.     foreach hit $matches {
  612.         set pos [lindex $hit 1]
  613.         set top [lindex $hit 2] 
  614.         set bottom [lindex $hit 3]
  615.         while {[set failure [expr {[getFldName $pos $top] != $fld}]]  && 
  616.             ![catch {search -f 1 -r 1 -i 1 -m 0 -l $bottom -s -- $reg $pos} mtch]} {
  617.             set pos [lindex $mtch 1]
  618.         }
  619.         if {!$failure} { lappend vals [list $top $bottom] }
  620.     }
  621.     
  622.     if {[llength $vals] >0} {
  623.         writeEntries $vals 0
  624.     } else {
  625.         message "No matching entries were found"
  626.     }
  627.     
  628. }
  629.  
  630. ###########################################################################
  631. # Sort all of the entries based on one of various criteria.
  632. #
  633. proc bibSortProc {menu item} {
  634.     if {$item == "citeKey"} {
  635.         sortByCiteKey
  636.     } elseif  {$item == "firstAuthor,Year"} {
  637.         sortByAuthors 0 0
  638.     } elseif  {$item == "lastAuthor,Year"} {
  639.         sortByAuthors 1 0
  640.     } elseif  {$item == "year,FirstAuthor"} {
  641.         sortByAuthors 0 1
  642.     } elseif  {$item == "year,LastAuthor"} {
  643.         sortByAuthors 1 1
  644.     }
  645. }
  646.  
  647. ###########################################################################
  648. # Sort the file marks. (These operations are also available under the
  649. # "Search:NamedMarks" menu)
  650. #
  651. proc markSortProc {menu item} {
  652.     if {$item == "alphabetically"} {
  653.         sortMarksFile
  654.     } elseif  {$item == "byPosition"} {
  655.         orderMarks
  656.     }
  657. }
  658.  
  659. ###########################################################################
  660. # Sort all of the entries in the file alphabetically by author.
  661. #
  662. proc sortByAuthors {{lastAuthorFirst 0} {yearFirst 0}} {
  663.     global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
  664.     set bibSegStr $BibmodeVars(segregateStrings)
  665.     
  666.     set matches [findEntries $bibTopPat]
  667.     set crossrefs [listCrossrefs]
  668.     set strings [listStrings]
  669.     
  670.     set vals {}
  671.     set others {}
  672.     set refs {}
  673.     set strs {}
  674.     
  675.     set beg [maxPos]
  676.     set end 0
  677.     
  678.     foreach hit $matches {
  679.         set pos [lindex $hit 1]
  680.         set top [lindex $hit 2] 
  681.         set bottom [lindex $hit 3]
  682.         set entry [getText $top $bottom]
  683.         regsub -all "\[\n\r\]+" $entry { } entry
  684.         regsub -all "\[     \]\[     \]+" $entry { } entry
  685.         regsub {[,     ]*[\)\}][     ]*$} $entry { } entry
  686.         if {[regexp $bibTopPat1 $entry allofit citeKey]} {
  687.             set citeKey [string tolower $citeKey]
  688.             set keyExists 1
  689.         } else {
  690.             set citekey ""
  691.             set keyExists 0
  692.         }
  693.         
  694.         if {$keyExists && [lsearch -exact $crossrefs $citeKey] >= 0} {
  695.             lappend refs [list $pos $top $bottom]
  696.         } elseif {$bibSegStr && $keyExists && [lsearch -exact $strings $citeKey] >= 0} {
  697.             lappend strs [list $citeKey $top $bottom]        
  698.         } else {
  699.             if {![catch {getFldValue $entry author} fldval]} {
  700.                 if {[catch {getFldValue $entry year} year]} { set year 9999 }
  701.                 lappend vals [list [authSortKey $fldval $lastAuthorFirst $year $yearFirst] $top $bottom]
  702.             } else {
  703.                 lappend others [list $pos $top $bottom]
  704.             }
  705.         }
  706.         if {$top < $beg} {set beg $top}
  707.         if {$bottom > $end} {set end $bottom}
  708.     }
  709.     
  710.     if {$bibSegStr} {
  711.         set result [concat $strs $others [lsort $vals] $refs]
  712.     } else {
  713.         set result [concat $others [lsort $vals] $refs]
  714.     }
  715.     
  716.     if {[llength $result] >0} {
  717.         writeEntries $result 1 $beg $end
  718.     } else {
  719.         message "No results of author sort !!??"
  720.     }
  721. }
  722.  
  723. ###########################################################################
  724. # Return a list of the cite-keys of all cross-referenced entries.
  725. #
  726. proc listStrings {} {
  727.     global bibTopPat bibTopPat1 bibTopPat2
  728.     set matches [findEntries {^[    ]*@string *[\{\(]} 0]
  729.  
  730.     message "scanning for @strings╔"
  731.     foreach hit $matches {
  732.         set top [lindex $hit 2] 
  733.         set bottom [lindex $hit 3]
  734.         set entry [getText $top $bottom]
  735.         regsub -all "\[\n\r\]+" $entry { } entry
  736.         regsub -all "\[     \]\[     \]+" $entry { } entry
  737.         regsub {[,     ]*[\)\}][     ]*$} $entry { } entry
  738.         regexp $bibTopPat1 $entry allofit citekey
  739.         set citekey [string tolower $citekey]
  740.         if {[catch {incr strings($citekey)} num]} {
  741.             set strings($citekey) 1
  742.         }
  743.     }
  744.     if {[catch {lsort [array names strings]} res]} {
  745.         set res {}
  746.     }
  747.     message ""
  748.     return $res
  749. }
  750.  
  751. ###########################################################################
  752. # Return a list of the cite-keys of all cross-referenced entries.
  753. #
  754. proc listCrossrefs {} {
  755.     set matches [findEntries {crossref}]
  756.     catch {unset crossrefs}
  757.  
  758.     message "scanning for crossrefs╔"
  759.     foreach hit $matches {
  760.         set top [lindex $hit 2] 
  761.         set bottom [lindex $hit 3]
  762.         set entry [getText $top $bottom]
  763.         regsub -all "\[\n\r\]+" $entry { } entry
  764.         regsub -all "\[     \]\[     \]+" $entry { } entry
  765.         regsub {[,     ]*[\)\}][     ]*$} $entry { } entry
  766.         if {![catch {getFldValue $entry crossref} fldval]} {
  767.             set fldval [string tolower $fldval]
  768.             if {[catch {incr crossref($fldval)} num]} {
  769.                 set crossrefs($fldval) 1
  770.             }
  771.         }
  772.     }
  773.     if {[catch {lsort [array names crossrefs]} res]} {
  774.         set res {}
  775.     }
  776.     message ""
  777.     return $res
  778. }
  779.  
  780. ###########################################################################
  781. # Create a sort key from an author list.  When sorting entries by author, 
  782. # performing the sort using keys should be faster than reparsing the author 
  783. # lists for every comparison (the old method :-( ).
  784. #
  785. proc authSortKey {authList lastAuthorFirst {year {}} {yearFirst 0}} {
  786.     global BibmodeVars
  787.     set pat1 {\\.\{([A-Za-z])\}}
  788.     set pat2 {\{([^\{\}]+) ([^\{\}]+)\}}
  789.  
  790. # Remove enclosing braces, quotes, or whitespace
  791.     set auths %[string trim $authList {{}"     }]&
  792. # Remove TeX codes for accented characters
  793.     regsub -all $pat1 $auths {\1} auths
  794. # Concatenate strings enclosed in braces
  795.     while {[regsub -all $pat2 $auths {{\1\2}} auths]} {}
  796. # Remove braces (curly and square)
  797.     regsub -all {[][\{\}]} $auths {} auths
  798. #    regsub -all {,} $auths { ,} auths
  799. # Replace 'and's with begin-name/end-name delimiters
  800.     regsub -all {[     ]and[     ]} $auths { \&% } auths
  801. # Put last name first in name fields without commas
  802.     regsub -all {%([^\&,]+) ([^\&, ]+) *\&} $auths {%\2,\1\&} auths
  803. # Remove begin-name delimiters
  804.     regsub -all {%} $auths {} auths
  805. # Remove whitespace surrounding name separators
  806.     regsub -all {[     ]*\&[     ]*} $auths {\&} auths
  807. # Replace whitespace separating words with shrieks 
  808.     regsub -all {[     ,]+} $auths {!} auths
  809. # If desired, move last author to head of sort key
  810.     if {$lastAuthorFirst} {
  811.         regsub {(.*)&([^&]+)&?$} $auths {\2\&\1} auths
  812.     }
  813. # If provided, sort by year (descending order) as well
  814.     regsub {^[^0-9]*([0-9]*).*$} $year {\1} year
  815.     if {$year != {}} {
  816.         if {$BibmodeVars(descendingYears)} { catch {set year [expr 9999-$year]} }
  817.         if {$yearFirst} {
  818.             set auths "$year&$auths"
  819.         } else {        
  820.             regsub {^([^&]+)(&?)} $auths "\\1\\&${year}\\2" auths
  821.         }
  822.     }
  823.         
  824.     return $auths
  825. }
  826.  
  827. ###########################################################################
  828. # Sort all of the entries in the file alphabetically by their cite-keys.
  829. #
  830. proc sortByCiteKey {} {
  831.     global bibTopPat bibTopPat1 bibTopPat2 BibmodeVars
  832.     set bibSegStr $BibmodeVars(segregateStrings)
  833.     
  834.     set matches [findEntries $bibTopPat]
  835.     set crossrefs [listCrossrefs]
  836.     set strings [listStrings]
  837.  
  838.     set begEntries [maxPos]
  839.     set endEntries 0
  840.     
  841.     set strs {}
  842.     set vals {}
  843.     set refs {}
  844.         
  845.     foreach hit $matches {
  846.         set beg [lindex $hit 0]
  847.         set end [lindex $hit 1]
  848.         set top [lindex $hit 2] 
  849.         set bottom [lindex $hit 3]
  850.         if {[regexp $bibTopPat1 [getText $top $bottom] allofit citekey]} {
  851.             set citekey [string tolower $citekey]
  852.             set keyExists 1
  853.         } else {
  854.             set citekey "000000$beg"
  855.             set keyExists 0
  856.         }
  857.         
  858.         if {$keyExists && [lsearch -exact $crossrefs $citekey] >= 0} {
  859.             lappend refs [list $top $top $bottom]
  860.         } elseif {$keyExists && $bibSegStr && [lsearch -exact $strings $citekey] >= 0} {
  861.             lappend strs [list $citekey $top $bottom]        
  862.         } else {
  863.             lappend vals [list $citekey $top $bottom]
  864.         }
  865.  
  866.         if {$top < $begEntries} {set begEntries $top}
  867.         if {$bottom > $endEntries} {set endEntries $bottom}
  868.     }
  869.  
  870.     if {$bibSegStr} {
  871.         set result [concat $strs [lsort $vals] $refs]
  872.     } else {
  873.         set result [concat [lsort $vals] $refs]
  874.     }
  875.     
  876.     if {[llength $result] >0} {
  877.         writeEntries $result 1 $begEntries $endEntries
  878.     } else {
  879.         message "No results of cite-key sort !!??"
  880.     }
  881. }
  882.  
  883. ###########################################################################
  884. # Search for all entries matching a given regular expression.  The results
  885. # are returned in a list, each element of which is a list of four integers:
  886. # the beginning and end of the matching entry and the beginning and end of
  887. # the matching string.  Adapted from "matchingLines" in "misc.tcl".
  888. #
  889. proc findEntries {reg {casesen 1}} {
  890.     if {![string length $reg]} return
  891.     
  892.     set pos 0   
  893.     set result {}                             
  894.     while {![catch {search -f 1 -r 1 -m 0 -i $casesen -s $reg $pos} mtch]} {
  895.         set entry [getEntry [lindex $mtch 0]]
  896.         lappend result [concat $mtch $entry]
  897.         set pos [lindex $entry 1]
  898.     }
  899.     return $result
  900. }
  901.  
  902. ###########################################################################
  903. #  Return a list containing the data for the current entry, indexed by
  904. #  the parameter names, e.g., "author", "year", etc.  Index names for the 
  905. #  entry type and cite-key are "type" and "citekey". 
  906. #
  907. proc getFields {pos} {
  908.      global bibTopPat bibTopPat1 bibTopPat2 bibTopPat3                                                                                                                    ░La Cie 191:Writing ─:Alpha 6.0b20 ─:Tcl:SystemCode:bibtex.tcl
  909.     set fldPat {[     ]*([a-zA-Z]+)[     ]*=[     ]*}
  910.  
  911.     set limits [getEntry $pos]
  912.     set top [lindex $limits 0]
  913.     set bottom [lindex $limits 1]
  914.     
  915.     set entry [getText $top $bottom]
  916.     regsub -all "\[\n\r\]+" $entry { } entry
  917.     regsub -all "\[     \]\[     \]+" $entry { } entry
  918. #
  919.     regsub {[,     ]*[\)\}][     ]*$} $entry { } entry
  920.  
  921.     if {[regexp -indices $bibTopPat2 $entry mtch theType theKey ]} {
  922.         set key [string range $entry [lindex $theKey 0] [lindex $theKey 1]]
  923.         set theRest [expr 1 + [lindex $mtch 1]]
  924.     } elseif {[regexp -indices $bibTopPat3 $entry mtch theType aField]} {
  925.         set key {}
  926.         set theRest [lindex $aField 0]
  927.     } else {
  928.         error "Invalid entry"
  929.     }
  930.     lappend names type
  931.     set type [string tolower [string range $entry [lindex $theType 0] [lindex $theType 1]]]
  932.     lappend data [list $type]
  933.  
  934.     lappend names citekey
  935.     lappend data $key
  936.     
  937.     set entry ",[string range $entry $theRest end]"
  938.     set fldPat {,[     ]*([^ =,]+)[     ]*=[     ]*}
  939.     set name {}
  940.     while {[regexp -indices $fldPat $entry mtch sub1]} {
  941.         set nextName [string range $entry [lindex $sub1 0] [lindex $sub1 1]]
  942.         lappend names [string tolower $nextName]
  943.         if {$name != ""} { 
  944.             set prevData [string range $entry 0 [expr [lindex $mtch 0]-1]]
  945.             lappend data [breakIntoLines [bibFieldData $prevData]]
  946.         }    
  947.         set name $nextName
  948.         set entry [string range $entry [expr [lindex $mtch 1]+1] end]
  949.     }
  950.  
  951.     lappend data [breakIntoLines [bibFieldData $entry]]
  952.     
  953.     return [list $names $data]
  954. }
  955.  
  956. proc bibFieldData {text} {
  957.     set text [string trim $text {     ,#}]
  958.     set text1 [string trim $text {\{\}\"     }]            
  959.     
  960.     if {[string match {*[\{\}\"]*} $text1]} {
  961.         set words [parseWords $text]
  962.         if {[llength $words]==1} {
  963.             regsub {^[\{\"\']} $text {} text
  964.             regsub {[\}\"\']$} $text {} text
  965.         }
  966.     } else {
  967.         set text $text1            
  968.     }
  969.     return $text
  970. }
  971.  
  972.  
  973. ###########################################################################
  974. # Extract the data from the indicated field of an entry, which is passed 
  975. # as a single string.  This version tries to be completely general, 
  976. # allowing nested braces within data fields and ignoring escaped 
  977. # delimiters.  (derived from proc getField).
  978. #
  979. proc getFldValue {entry fldname} {
  980.     set fldPat "\[     \]*${fldname}\[     \]*=\[     \]*"
  981.     set fldPat2 {,[     ]*([^ =,]+)[     ]*=[     ]*}
  982.     set slash "\\"
  983.     set qslash "\\\\"
  984.     
  985.     set ok [regexp -indices -nocase $fldPat $entry mtch]
  986.     if {$ok} {
  987.         set pos [expr [lindex $mtch 1] + 1]
  988.         set entry [string range $entry $pos end]
  989.         
  990.         if {[regexp -indices $fldPat2 $entry mtch sub1]} {
  991.             set entry [string range $entry 0 [expr [lindex $mtch 0]-1]]
  992.         } 
  993.         set fld [bibFieldData $entry]
  994.         
  995.         return $fld
  996.         
  997.     } else {
  998.         error "field not found"
  999.     }
  1000. }
  1001.  
  1002. ###########################################################################
  1003. # Parse the entry around position "pos" and rewrite it to the original 
  1004. # buffer in a canonical format
  1005. #
  1006. proc formatEntry {} {
  1007.     global useBrace bibOpenQuote bibCloseQuote 
  1008.     global bibOpenEntry bibCloseEntry bibIndent
  1009.     set spc "                           "
  1010.     
  1011.     bibFormatSetup
  1012.     
  1013.     set pos [getPos]
  1014.     set limits [getEntry $pos]
  1015.     set top [lindex $limits 0]
  1016.     set bottom [lindex $limits 1]
  1017.     
  1018.     if {![catch {bibFormatEntry $pos} result]} {
  1019.         set oldEntry [getText $top $bottom]
  1020.         if {$result != $oldEntry} {
  1021.             deleteText $top $bottom 
  1022.             insertText $result
  1023.         } 
  1024.         goto $top 
  1025.         nextEntry
  1026.     } else {
  1027.         message "Couldn't format this entry for some reason"
  1028.     }
  1029. }
  1030.  
  1031. ###########################################################################
  1032. # Parse the entry around position "pos" and rewrite it to the original 
  1033. # buffer in a canonical format
  1034. #
  1035. proc formatAllEntries {} {
  1036.     global useBrace bibOpenQuote bibCloseQuote 
  1037.     global bibOpenEntry bibCloseEntry bibIndent
  1038.     set spc "                           "
  1039.     
  1040.     bibFormatSetup
  1041.     
  1042.     # This little dance handles the case that the first 
  1043.     # entry starts on the first line
  1044.     #
  1045.     set hit [getEntry [getPos]]
  1046.     if {[lindex $hit 0] == [lindex $hit 1]} {
  1047.         nextEntry
  1048.         set hit [getEntry [getPos]]
  1049.     }
  1050.     
  1051.     while {[getPos] < [lindex $hit 1]} {
  1052.         set top [lindex $hit 0] 
  1053.         set bottom [lindex $hit 1]
  1054.         
  1055.         if {![catch {bibFormatEntry $top} result]} {
  1056.             set oldEntry [getText $top $bottom]
  1057.             if {$result != $oldEntry} {
  1058.                 deleteText $top $bottom 
  1059.                 insertText $result
  1060.             } 
  1061.         }
  1062.         goto $top
  1063.         nextEntry
  1064.         set hit [getEntry [getPos]]
  1065.     }
  1066. }
  1067.  
  1068. ###########################################################################
  1069. # Parse the entry around position "pos" and rewrite it in a canonical format.
  1070. # The formatted entry is returned.
  1071. #
  1072. proc bibFormatEntry {pos} {
  1073.     global useBrace bibOpenQuote bibCloseQuote 
  1074.     global bibOpenEntry bibCloseEntry bibIndent
  1075.     global rqdFld optFld BibmodeVars bibAbbrevs
  1076.     set spc "                           "
  1077.     #    
  1078.     #    note: calling proc must call "bibFormatSetup" before calling "bibFormatEntry"
  1079.     #
  1080.     set limits [getEntry $pos]
  1081.     set top [lindex $limits 0]
  1082.     set bottom [lindex $limits 1]
  1083.  
  1084.     if {[catch {getFields $pos} flds]} {
  1085.         error "bibFormatEntry: Getflds couldn't find any"
  1086.     }
  1087.     
  1088.     set names [lindex $flds 0]
  1089.     set vals [lindex $flds 1]
  1090.     set nfld [llength $names]
  1091.     
  1092.     set type [string tolower [lindex $vals 0]]
  1093.     set citekey [lindex $vals 1]
  1094. #     message "$citekey"
  1095.     # Don't process @string entries
  1096.     if {$type == "string"} {
  1097.         set lines [getText $top $bottom]
  1098.         return $lines
  1099.     }
  1100.     # Find length of longest field name
  1101.     set nmlen 0
  1102.     foreach nm $names {
  1103.         set len [string length $nm]
  1104.         if {$len > $nmlen} { set nmlen $len }
  1105.         if {![info exists useBrace($nm)]} { set useBrace($nm) 0 }
  1106.     }
  1107.     
  1108.     # Format first line
  1109.     set lines "@${type}${bibOpenEntry}${citekey},\r"
  1110.     
  1111.     # Format each field on a separate line
  1112.     for {set ifld 2} {$ifld < $nfld} {incr ifld} { 
  1113.     set nm [lindex $names $ifld]
  1114.     set vl [lindex $vals $ifld]
  1115.     if {$vl != "" || ! $BibmodeVars(zapEmptyFields) || 
  1116.             [lsearch $rqdFld($type) $nm] >= 0} {
  1117.         set pad [expr $nmlen - [string length $nm]]
  1118.         
  1119.         if {$BibmodeVars(alignEquals)} {
  1120.             set pref "${bibIndent}$nm[string range $spc 1 $pad] ="
  1121.         } else {
  1122.             set pref "${bibIndent}$nm =[string range $spc 1 $pad]"
  1123.         }
  1124.         set ind [string range $spc 1 [string length $pref]]
  1125.         
  1126.         # Delimit field, if appropriate
  1127.         set noBrace [expr ($useBrace($nm) == 0 && [isNum $vl]) || [hasCat $vl]]
  1128.         if {$noBrace == 0 && [string first " " $vl] < 0} {
  1129.             set noBrace [expr [lsearch $bibAbbrevs [string tolower $vl]] >= 0]
  1130.         }
  1131.         if {$noBrace != 0} {
  1132.             set vl "$vl,"
  1133.         } else {
  1134.             set vl "${bibOpenQuote}${vl}${bibCloseQuote},"
  1135.         }
  1136.         
  1137.         set pieces [split $vl "\r"]
  1138.         append lines "$pref [lindex $pieces 0]\r"
  1139.         foreach piece [lrange $pieces 1 end] {
  1140.             append lines "$ind  $piece\r"
  1141.         }
  1142.     }
  1143. }
  1144.     append lines "$bibCloseEntry\r"
  1145.     return $lines
  1146. }
  1147.  
  1148. ###########################################################################
  1149. # Get the name of the field that starts before the given position,  
  1150. # $pos.  The positions $top and $bottom restrict the range of the 
  1151. # search for the beginning and end of the field; typically, $top and
  1152. # $bottom will be the limits of a given entry.
  1153. #
  1154. proc getFldName {pos top} {
  1155.     set fldPat {[,     ]+([^     =,\{\}\"\']+)[     ]*=[     ]*}
  1156.     if {![catch {search -f 0 -r 1 -m 0 -i 1 -s -limit $top "$fldPat" $pos} mtch]} {
  1157.         set theText [eval getText $mtch]
  1158.         regexp -nocase $fldPat $theText allofit fldnam
  1159.         return $fldnam
  1160.     } else {
  1161.         return {citekey}
  1162.     }
  1163. }
  1164.  
  1165. ###########################################################################
  1166. #  Set the quote characters for quoted fields based on the value of the 
  1167. #  flag $bibUseBrace
  1168. proc bibFieldDelims {} {
  1169.     global BibmodeVars bibOpenQuote bibCloseQuote
  1170.     if {$BibmodeVars(fieldBraces)} then {
  1171.         set bibOpenQuote "{"
  1172.         set bibCloseQuote "}" 
  1173.     } else {
  1174.         set bibOpenQuote {"} 
  1175.         set bibCloseQuote {"} 
  1176.     }
  1177. }
  1178.  
  1179. proc bibEntryDelims {} {
  1180.     global BibmodeVars bibOpenEntry bibCloseEntry
  1181.     if {$BibmodeVars(entryBraces)} then {
  1182.         set bibOpenEntry "{"
  1183.         set bibCloseEntry "}" 
  1184.     } else {
  1185.         set bibOpenEntry "("
  1186.         set bibCloseEntry ")"
  1187.     }
  1188. }
  1189.  
  1190. proc isBibFile {} {
  1191.     set fileName [lindex [winNames -f] 0]   
  1192.     set ext [file extension $fileName]
  1193.     return [string match ".bib" [string tolower $ext]] 
  1194. }
  1195.  
  1196. proc hasNumVal {str} {
  1197.     expr ! [catch {expr $str}]
  1198. }
  1199. proc isNum {str} {
  1200.     regexp {^[     ]*[0-9]+[     ]*$} $str mtch
  1201. }
  1202. proc hasCat {str} {
  1203.     regexp {\#} $str mtch
  1204. }
  1205.  
  1206. ###########################################################################
  1207. # Take a list of lists that point to selected entries and copy these into
  1208. # a new window.  The beginning and ending positions for each entry must 
  1209. # be the last two items in each sublist.  The rest of the sublists are
  1210. # ignored.  It is assumed that each sublist has the same number of items.
  1211. #
  1212. proc writeEntries {entryPos nondestructive {beg {0}} {end {-1}}} {
  1213.         global BibmodeVars
  1214.         if {$end < 0} {set end [maxPos]}
  1215.         set llen [expr [llength [lindex $entryPos 0]] - 1]
  1216.         set llen1 [expr $llen-1]
  1217.         foreach entry $entryPos {
  1218.             set limits [lrange $entry $llen1 $llen]
  1219.             append lines [eval getText $limits]
  1220.         }
  1221.         set overwriteOK [expr $nondestructive || ! [isBibFile]]
  1222.         if {$BibmodeVars(overwriteBuffer) && $overwriteOK} {
  1223.             deleteText $beg $end
  1224.             insertText $lines
  1225.             goto $beg
  1226.         } else {
  1227.             set begLines [getText 0 [lineStart $beg]]
  1228.             set endLines [getText [nextLineStart $end] [maxPos]]
  1229.             new -n {*BibTeX Sort/Search*}
  1230.             newMode Bib
  1231.             insertText $begLines
  1232.             insertText $lines
  1233.             insertText $endLines
  1234.             goto $beg
  1235.             setWinInfo dirty 0
  1236.             catch shrinkWindow
  1237.         }
  1238. }
  1239.  
  1240. ###########################################################################
  1241. # Set a named mark for each entry, using the cite-key name
  1242. #
  1243. proc BibMarkFile {} {
  1244.     global BibmodeVars
  1245.      global bibTopPat bibTopPat1 bibTopPat2                                                                                                                        ░La Cie 191:Writing ─:Alpha 6.0b20 ─:Tcl:SystemCode:bibtex.tcl
  1246.     set pos 0
  1247.     while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat1 $pos} res]} {
  1248.         set start [lindex $res 0]
  1249.         set end [nextLineStart $start]
  1250.         set text [getText $start $end]
  1251.         set lab ""
  1252.         if {[regexp $bibTopPat2 $text mtch type citekey]} {
  1253.             if {[string tolower $type] != "string" || $BibmodeVars(markStrings)} { 
  1254.                 setNamedMark $citekey [lineStart [expr $start - 1]] $start $start
  1255.             }
  1256.         }
  1257.         set pos $end
  1258.     }
  1259. }
  1260.  
  1261. ###########################################################################
  1262. # Report the number of entries of each type
  1263. #
  1264. proc countEntries {} {
  1265.     global entryNames
  1266.      global bibTopPat bibTopPat1 bibTopPat2                                                                                                                        ░La Cie 191:Writing ─:Alpha 6.0b20 ─:Tcl:SystemCode:bibtex.tcl
  1267.     
  1268.     set pos 0
  1269.     set count 0
  1270.     catch {unset type}
  1271.     
  1272.     while {![catch {search -f 1 -r 1 -m 0 -i 0 -s $bibTopPat $pos} res]} {
  1273.         incr count
  1274.         set start [lindex $res 0]
  1275.         set end [nextLineStart $start]
  1276.         set text [getText $start $end]
  1277.         set lab ""
  1278.         if {[regexp $bibTopPat $text mtch entryType]} {
  1279.             set entryType [string tolower $entryType]
  1280.             if {[catch {incr type($entryType)} num]} {
  1281.                 set type($entryType) 1
  1282.             }
  1283.         }
  1284.         set pos $end
  1285.     }
  1286.     new -n {*BibTeX Statistics*}
  1287.     newMode Bib
  1288.     foreach name [lsort [array names type]] {
  1289.         if {$type($name) > 0} {
  1290.             append lines [format "%4.0d  %s\n" $type($name) $name]
  1291.         }
  1292.     }
  1293.     append lines "----  -----------------\n"
  1294.     append lines [format "%4.0d  %s\n" $count "Total entries"]
  1295.     insertText $lines
  1296.     goto 0
  1297.     setWinInfo dirty 0
  1298.     catch {shrinkWindow 1}
  1299. }
  1300. #--------------------------------------------------------------------------
  1301. # command-double-clicking:
  1302. #--------------------------------------------------------------------------
  1303.  
  1304. ###########################################################################
  1305. # In Bib mode, Cmd-double-clicks resolve abbrevs and cross-refs
  1306. #
  1307. proc BibDblClick {from to} {
  1308.     global bibTopPat bibTopPat1 bibTopPat2
  1309.     
  1310.     set limits [getEntry $from]
  1311.     set top [lindex $limits 0]
  1312.     set bottom [lindex $limits 1]
  1313.  
  1314.     # Extend selection to largest string that could be an entry reference
  1315.     set text [string trim [eval getText [BibExtendClick $from $to $top $bottom]]]
  1316.     
  1317.     # Get the citekey of current entry, so we can avoid jumping to it    
  1318.     set citekey {}
  1319.     regexp $bibTopPat2 [getText $top $bottom] mtch type citekey ]
  1320.     set fldName [getFldName $from $top]
  1321.  
  1322.     if {[string length $text] == 0 || $text == $citekey || $fldName == $text || 
  1323.         ($fldName == "citekey" && [string tolower $type] != "string")} {
  1324.         message "Command-double-click on abbreviations and crossref arguments"
  1325.         return
  1326.     }
  1327.  
  1328.     # Jump to the mark for the specified citation, if a mark exists...
  1329.     # ...otherwise, do an ordinary search for the cite key
  1330.     pushMark    
  1331.     set searchPat "$bibTopPat\[     \]*[quoteExpr $text]\[     ,\}\)\]"
  1332.     if {![catch {search -f 1 -r 1 -i 1 -m 0 $searchPat 0} mtch]} {
  1333.         goto [lindex $mtch 0]
  1334.     } else {
  1335.         popMark
  1336.         select $from $to
  1337.         if {$fldName == "crossref"} {
  1338.             message "Cross-reference \"$text\" not found"
  1339.         } else {
  1340.             message "Command-double-click on abbreviations and crossref arguments"
  1341.         }
  1342.         return
  1343.     }
  1344.     message "Use Ctl-. to return to original position"
  1345.     return
  1346. }
  1347.  
  1348. # Extend the selection around the initial selection {$from,$to}
  1349. # Extension is restricted to the range {$top,$bottom} (the current entry)
  1350. proc BibExtendClick {from to top bottom} {
  1351.     if {$to == 0} { set to $from }
  1352.     set result [list $from $to]
  1353.     if {![catch {search -f 0 -r 1 -s -m 0 -l $top "\[,\{\]\"\'=" $from} mtch0]} {
  1354.         if {![catch {search -f 1 -r 1 -s -m 0 -l $bottom "\[,\}\]\"\'=" $to} mtch1]} {
  1355.             set from [lindex $mtch0 1]
  1356.             set to [lindex $mtch1 0]
  1357.             # Check for illegal chars embedded in the selection
  1358.             if {[regexp "\[\{\}\]=" [getText $from $to]] == 0} {
  1359.                 set result [list $from $to]
  1360.             }
  1361.         }
  1362.     }
  1363.     return $result
  1364. }
  1365.  
  1366.  
  1367. ###########################################################################
  1368.  
  1369. proc dummyBibTeX {} {
  1370.     global BibmodeVars TeXmodeVars
  1371. #     if {$BibmodeVars(convert8bitAscii2TeX) != $TeXmodeVars(convert8bitAscii2TeX)} {
  1372. #         set BibmodeVars(convert8bitAscii2TeX) $TeXmodeVars(convert8bitAscii2TeX)
  1373. #     }    
  1374. }
  1375.  
  1376. #